package Network;

import java.io.*;
import java.util.*;

import Control.*;
import Main.*;

/**
 * Class for processings between bytestream and objects;
 * Also handles state request algorithm.
 * @author hell
 */
public class NetworkProtocol {

    // Timer for handling request timeout.
    static public Timer t1;
    static public boolean IsInitializing = false;

    // For buffering non-InitReply packets while initializing.

    //static private Enumeration<Controller> initbuf = null; // add() ??
    static private Vector<Command> initbuf;
    // *! ByteBuffer / Buffer; Vector<Controller>; ListIterator, 
    /**
     * Processes packet from NetworkLayer
     * Currently makes Controller-inherited object and call its "preform" method.
     * @throws IOException
     */
    public static void ProcessPacket(byte[] d) throws IOException {
        Main.PrintDebug(3, "NetworkLayer: received packet");
        try {
            ByteArrayInputStream bais = new ByteArrayInputStream(d);
            ObjectInputStream ois = new ObjectInputStream(bais);
            //Attention: the type of ver (variable) is Controller (not Version)
            Command ver = (Command) ois.readObject();
            ois.close();
            bais.close();

            // Probably need to check IsInitializing here and buffer all non-InitReply packets.
            if (IsInitializing) {
                // Check if ver is InitReply
                // *? Buffer only Version&Stepdown packets?
                if (ver.getClass() != InitReply.class) {
                    Main.PrintDebug(3, "NetworkProtocol: Received non-InitReply during initialization");
                    initbuf.add(ver); // Add it to buffer
                    return; // ...and don't process it.
                }

            }

            //execute it.
            ver.perform();
        } catch (ClassNotFoundException e) {
            Main.PrintDebug(7, "NetworkLayer: Unknown packet received");
        }
    }

    /**
     * Sends version as well as game command to network.
     * Currently creates byte array from Command and sends it to NetworkLayer.
     */
    public static void SendCommand(Command command) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(command);
        oos.flush();
        oos.close();
        baos.flush();
        baos.close();
        //byte[] BA = baos.toByteArray();
        NetworkLayer.SendData(baos.toByteArray());
    }

    public static void InitStateRequest() throws IOException {
        // Need: send status request and wait for either reply or timeout.
        // Timeout might be implemented by wait() and buffer for packets;
        // * possibly need to buffer all packets while waiting for status reply and process them later.
        // *? this might be processed inside Main().
        int reqtimeout = 1500; // Request timeout;
        NetworkProtocol.t1 = new Timer();
        NetworkProtocol.t1.schedule(new TimerTask() { // Declare function to be run at timeout.

            public void run() {

                /*NetworkProtocol.IsInitializing = false;
                t1.cancel();*/ //Duplication of code
                StopInit();
                Controller.Initialize();
                ProcessInitBuf();
            // *! Process initbuf.
            }
        }, reqtimeout);

        // Initialize initbuf;
        initbuf = new Vector<Command>();

        NetworkProtocol.IsInitializing = true;
        // *! create and send state request here.
        // (need a state request class)
        InitRequest req = new InitRequest();
        NetworkProtocol.SendCommand(req);
    }

    /**
     * Stops the initialization process.
     */
    public static void StopInit() {
        t1.cancel();
        IsInitializing = false;
    }

    /**
     * Processes buffer of packets, received during initialization.
     */
    public static void ProcessInitBuf() {
        if (IsInitializing) {
            // Process initbuf:
            for (int i = 0; i < initbuf.size(); i++) {
                initbuf.get(i).perform();
            }
            // Destroy initbuf:
            initbuf = null;
        }
    }
}
